home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mint110s / mem.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  40KB  |  1,707 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993,1994 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /*
  8.  * mem.c:: routines for managing memory regions
  9.  */
  10.  
  11. #include "mint.h"
  12. #include "fasttext.h" /* for line A stuff */
  13.  
  14. #ifndef VgetSize
  15. extern long xbios();
  16. #define VgetSize(mode) xbios(91, (short)(mode))
  17. #define Vsetmode(mode) xbios(88, (short)(mode))
  18. #endif
  19.  
  20. static long core_malloc P_((long, int));
  21. static void core_free P_((long));
  22.  
  23. /* macro for testing whether a memory region is free */
  24. #define ISFREE(m) ((m)->links == 0)
  25.  
  26. /*
  27.  * list of shared text regions currently being executed
  28.  */
  29. SHTEXT *text_reg = 0;
  30.  
  31. /*
  32.  * initialize memory routines
  33.  */
  34.  
  35. /* initial number of memory regions */
  36. #define NREGIONS ((8*1024)/sizeof(MEMREGION))
  37.  
  38. /* number of new regions to allocate when the initial ones are used up */
  39. #define NEWREGIONS ((8*1024)/sizeof(MEMREGION))
  40.  
  41. static MEMREGION use_regions[NREGIONS+1];
  42. MEMREGION *rfreelist;
  43.  
  44. /* variable for debugging purposes; number of times we've needed
  45.  * to get new regions
  46.  */
  47. int num_reg_requests = 0;
  48.  
  49. /* these variables are set in init_core(), and used in
  50.  * init_mem()
  51.  */
  52. static ulong scrnsize, scrnplace;
  53. static SCREEN *vscreen;
  54.  
  55. void
  56. init_mem()
  57. {
  58.     int i;
  59.     MEMREGION *r;
  60.     long newbase;
  61.  
  62.     use_regions[NREGIONS].next = 0;
  63.     for (i = 0; i < NREGIONS; i++) {
  64.         use_regions[i].next = &use_regions[i+1];
  65.     }
  66.     rfreelist = use_regions;
  67.  
  68.     init_core();
  69.     init_swap();
  70.  
  71.     init_tables();            /* initialize MMU constants */
  72.  
  73.     /* mark all the regions in the core & alt lists as "invalid" */
  74.     for (r = *core; r; r = r->next) {
  75.         mark_region(r,PROT_I);
  76.     }
  77.     for (r = *alt; r; r = r->next) {
  78.         mark_region(r,PROT_I);
  79.     }
  80.  
  81.     /* make sure the screen is set up properly */
  82.     newbase = s_realloc(scrnsize);
  83.  
  84.     /* if we did get a new screen, point the new screen
  85.      * at the right place after copying the data
  86.      * if possible, save the screen to another buffer,
  87.      * since if the new screen and old screen overlap
  88.      * the blit will look very ugly.
  89.      * Note that if the screen isn't moveable, then we set
  90.      * scrnsize to a ridiculously large value, and so the
  91.      * s_realloc above failed.
  92.      */
  93.     if (newbase) {
  94.     /* find a free region for temp storage */
  95.         for (r = *core; r; r = r->next) {
  96.             if (ISFREE(r) && r->len >= scrnsize)
  97.                 break;
  98.         }
  99.  
  100.         if (r) {
  101.             quickmove((char *)r->loc, (char *)scrnplace, scrnsize);
  102.             Setscreen((void *)r->loc, (void *)r->loc, -1);
  103.             Vsync();
  104.             quickmove((char *)newbase, (char *)r->loc, scrnsize);
  105.         } else {
  106.             quickmove((char *)newbase, (char *)scrnplace, scrnsize);
  107.         }
  108.         Setscreen((void *)newbase, (void *)newbase, -1);
  109.     /* fix the cursor */
  110.         Cconws("\r\n"); 
  111.     }
  112. }
  113.  
  114. void
  115. restr_screen()
  116. {
  117.   long base = (long) Physbase ();
  118.   MEMREGION *r;
  119.  
  120.   if (base != scrnplace)
  121.     {
  122.       for (r = *core; r; r = r->next)
  123.     {
  124.       if (ISFREE (r) && r->len >= scrnsize)
  125.         break;
  126.     }
  127.       if (r)
  128.     {
  129.       quickmove ((char *) r->loc, (char *) base, scrnsize);
  130.       Setscreen ((void *) r->loc, (void *) r->loc, -1);
  131.       Vsync ();
  132.       quickmove ((char *) scrnplace, (char *) r->loc, scrnsize);
  133.     }
  134.       else
  135.     quickmove ((char *) scrnplace, (char *) base, scrnsize);
  136.       Setscreen ((void *) scrnplace, (void *) scrnplace, -1);
  137.       Cconws ("\r\n"); 
  138.     }
  139. }
  140.  
  141. /*
  142.  * init_core(): initialize the core memory map (normal ST ram) and also
  143.  * the alternate memory map (fast ram on the TT)
  144.  */
  145.  
  146. static MEMREGION *_core_regions = 0, *_alt_regions = 0,
  147.     *_ker_regions = 0;
  148.  
  149. MMAP core = &_core_regions;
  150. MMAP alt = &_alt_regions;
  151. MMAP ker = &_ker_regions;
  152.  
  153. /* note: add_region must adjust both the size and starting
  154.  * address of the region being added so that memory is
  155.  * always properly aligned
  156.  */
  157.  
  158. int
  159. add_region(map, place, size, mflags)
  160.     MMAP map;
  161.     ulong place, size;
  162.     unsigned mflags;    /* initial flags for region */
  163. {
  164.       MEMREGION *m;
  165.     ulong trimsize;
  166.  
  167.     TRACELOW(("add_region(map=%lx,place=%lx,size=%lx,flags=%x)",
  168.         map,place,size,mflags));
  169.  
  170.     m = new_region();
  171.     if (m == 0)
  172.         return 0;    /* failure */
  173.     m->links = 0;
  174.  
  175.     if (place & MASKBITS) {
  176.         /* increase place & shorten size by the amount we're trimming */
  177.         trimsize = (MASKBITS+1) - (place & MASKBITS);
  178.         if (size <= trimsize) goto lose;
  179.         size -= trimsize;
  180.         place += trimsize;
  181.     }
  182.  
  183.     /* now trim size DOWN to a multiple of pages */
  184.     if (size & MASKBITS) size &= ~MASKBITS;
  185.  
  186.     /* only add if there's anything left */
  187.     if (size) {
  188.         m->len = size;
  189.         m->loc = place;
  190.         m->next = *map;
  191.         m->mflags = mflags;
  192.         *map = m;
  193.     }
  194.     else {
  195.         /* succeed but don't do anything; dispose of region */
  196. lose:        dispose_region(m);
  197.     }
  198.     return 1;    /* success */
  199. }
  200.  
  201. static long
  202. core_malloc(amt, mode)
  203.     long amt;
  204.     int mode;
  205. {
  206.     static int mxalloc = -1;    /* does GEMDOS know about Mxalloc? */
  207.     long ret;
  208.  
  209.     if (mxalloc < 0) {
  210.         ret = (long)Mxalloc(-1L, 0);
  211.         if (ret == -32) mxalloc = 0;    /* unknown function */
  212.         else if (ret >= 0) mxalloc = 1;
  213.         else {
  214.             ALERT("GEMDOS returned %ld from Mxalloc", ret);
  215.             mxalloc = 0;
  216.         }
  217.     }
  218.     if (mxalloc)
  219.         return (long) Mxalloc(amt, mode);
  220.     else if (mode == 1)
  221.         return 0L;
  222.     else
  223.         return (long) Malloc(amt);
  224. }
  225.  
  226. static void
  227. core_free(where)
  228.     long where;
  229. {
  230.     Mfree((void *)where);
  231. }
  232.  
  233. void
  234. init_core()
  235. {
  236.     extern int FalconVideo;    /* set in main.c */
  237.     int scrndone = 0;
  238.     ulong size;
  239.     ulong place;
  240.     ulong temp;
  241.     void *tossave;
  242.  
  243.     tossave = (void *)core_malloc((long)TOS_MEM, 0);
  244.     if (!tossave) {
  245.         FATAL("Not enough memory to run MiNT");
  246.     }
  247.  
  248. /* initialize kernel memory */
  249.     place = (ulong)core_malloc(KERNEL_MEM, 3);
  250.     if (place != 0) {
  251.         nalloc_arena_add((void *)place,KERNEL_MEM);
  252.     }
  253.  
  254. /*
  255.  * find out where the screen is. We want to manage the screen
  256.  * memory along with all the other memory, so that Srealloc()
  257.  * can be used by the XBIOS to allocate screens from the
  258.  * end of memory -- this avoids fragmentation problems when
  259.  * changing resolutions.
  260.  */
  261. /* Note, however, that some graphics boards (e.g. Matrix)
  262.  * are unable to change the screen address. We fake out the
  263.  * rest of our code by pretending to have a really huge
  264.  * screen that can't be changed.
  265.  */
  266.     scrnplace = (long)Physbase();
  267.  
  268.     vscreen = (SCREEN *)((char *)lineA0() - 346);
  269.     if (FalconVideo) {
  270.     /* the Falcon can tell us the screen size */
  271.         scrnsize = VgetSize(Vsetmode(-1));
  272.     } else {
  273.     /* otherwise, use the line A variables */
  274.         scrnsize = (vscreen->maxy+1)*(long)vscreen->linelen;
  275.     }
  276.  
  277. /* check for a graphics card with fixed screen location */
  278. #define phys_top_st (*(ulong *)0x42eL)
  279.  
  280.     if (scrnplace >= phys_top_st) {
  281. /* screen isn't in ST RAM */
  282.         scrnsize = 0x7fffffffUL;
  283.         scrndone = 1;
  284.     } else {
  285.         temp = (ulong)core_malloc(scrnsize+256L, 0);
  286.         if (temp) {
  287.             (void)Setscreen((void *)-1L,
  288.                     (void *)((temp+511)&(0xffffff00L)), -1);
  289.             if ((long)Physbase() != ((temp+511)&(0xffffff00L))) {
  290.                 scrnsize = 0x7fffffffUL;
  291.                 scrndone = 1;
  292.             }
  293.             (void)Setscreen((void *)-1L, (void *)scrnplace, -1);
  294.             core_free(temp);
  295.         }
  296.     }
  297.  
  298. /* initialize ST RAM */
  299.     size = (ulong)core_malloc(-1L, 0);
  300.     while (size > 0) {
  301.         place = (ulong)core_malloc(size, 0);
  302.         if (!scrndone && (place + size == scrnplace)) {
  303.             size += scrnsize;
  304.             scrndone = 1;
  305.         }
  306.         if (!add_region(core, place, size, M_CORE))
  307.             FATAL("init_mem: unable to add a region");
  308.         size = (ulong)core_malloc(-1L, 0);
  309.     }
  310.  
  311.     if (!scrndone) {
  312.         (void)add_region(core, scrnplace, scrnsize, M_CORE);
  313.     }
  314.  
  315. /* initialize alternate RAM */
  316.     size = (ulong)core_malloc(-1L, 1);
  317.     while (size > 0) {
  318.         place = (ulong)core_malloc(size, 1);
  319.         if (!add_region(alt, place, size, M_ALT))
  320.             FATAL("init_mem: unable to add a region");
  321.         size = (ulong)core_malloc(-1L, 1);
  322.     }
  323.  
  324.     (void)Mfree(tossave);        /* leave some memory for TOS to use */
  325. }
  326.  
  327. /*
  328.  * init_swap(): initialize the swap area; for now, this does nothing
  329.  */
  330.  
  331. MEMREGION *_swap_regions = 0;
  332. MMAP swap = &_swap_regions;
  333.  
  334. void
  335. init_swap()
  336. {
  337. }
  338.  
  339. /*
  340.  * routines for allocating/deallocating memory regions
  341.  */
  342.  
  343. /*
  344.  * new_region returns a new memory region descriptor, or NULL
  345.  */
  346.  
  347. MEMREGION *
  348. new_region()
  349. {
  350.     MEMREGION *m, *newfrees;
  351.     int i;
  352.  
  353.     m = rfreelist;
  354.     if (!m) {
  355.         ALERT("new_region: ran out of free regions");
  356.         return 0;
  357.     }
  358.     assert(ISFREE(m));
  359.     rfreelist = m->next;
  360.     m->next = 0;
  361.  
  362. /* if we're running low on free regions, allocate some more
  363.  * we have to do this with at least 1 free region left so that get_region
  364.  * has a chance of working
  365.  */
  366.     if (rfreelist && !rfreelist->next) {
  367.         MEMREGION *newstuff;
  368.  
  369.         TRACELOW(("get_region: getting new region descriptors"));
  370.         newstuff = get_region(ker, NEWREGIONS*SIZEOF(MEMREGION), PROT_S);
  371.         if (!newstuff)
  372.             newstuff = get_region(alt,NEWREGIONS*SIZEOF(MEMREGION), PROT_S);
  373.         if (!newstuff)
  374.             newstuff = get_region(core, NEWREGIONS*SIZEOF(MEMREGION), PROT_S);
  375.         newfrees = newstuff ? (MEMREGION *)newstuff->loc : 0;
  376.         if (newfrees) {
  377.             num_reg_requests++;
  378.             newfrees[NEWREGIONS-1].next = 0;
  379.             newfrees[NEWREGIONS-1].links = 0;
  380.             for (i = 0; i < NEWREGIONS-1; i++) {
  381.                 newfrees[i].next = &newfrees[i+1];
  382.                 newfrees[i].links = 0;
  383.             }
  384.             rfreelist = newfrees;
  385.         } else {
  386.             DEBUG(("couldn't get new region descriptors!"));
  387.         }
  388.     }
  389.  
  390.     return m;
  391. }
  392.  
  393. /*
  394.  * dispose_region destroys a memory region descriptor
  395.  */
  396.  
  397. void
  398. dispose_region(m)
  399.     MEMREGION *m;
  400. {
  401.     m->next = rfreelist;
  402.     rfreelist = m;
  403. }
  404.  
  405. /*
  406.  * change_prot_status: change the status of a region to 'newmode'.  We're
  407.  * given its starting address, not its region structure pointer, so we have
  408.  * to find the region pointer; since this is illegal if proc doesn't own
  409.  * the region, we know we'll find the region struct pointer in proc->mem.
  410.  *
  411.  * If the proc doesn't own it, you get EACCDN.  There are no other errors.
  412.  * God help you if newmode isn't legal!
  413.  */
  414.  
  415. long
  416. change_prot_status(proc,start,newmode)
  417. PROC *proc;
  418. long start;
  419. int newmode;
  420. {
  421.     MEMREGION **mr;
  422.     int i;
  423.  
  424.     /* return EACCDN if you don't own the region in question */
  425.     if (!proc->mem) return EACCDN;
  426.  
  427.     for (mr = proc->mem, i = 0; i < proc->num_reg; i++, mr++) {
  428.     if ((*mr)->loc == start) goto found;
  429.     }
  430.     return EACCDN;
  431.  
  432. found:
  433.     mark_region(*mr,newmode);
  434.     return E_OK;
  435. }
  436.  
  437. /*
  438.  * virtaddr
  439.  * attach_region(proc, reg): attach the region to the given process:
  440.  * returns the address at which it was attached, or NULL if the process
  441.  * cannot attach more regions. The region link count is incremented if
  442.  * the attachment is successful.
  443.  */
  444.  
  445. virtaddr
  446. attach_region(proc, reg)
  447.     PROC *proc;
  448.     MEMREGION *reg;
  449. {
  450.     int i;
  451.     MEMREGION **newmem;
  452.     virtaddr *newaddr;
  453.  
  454.     TRACELOW(("attach_region %lx len %lx to pid %d",
  455.         reg->loc, reg->len, proc->pid));
  456.  
  457.     if (!reg || !reg->loc) {
  458.         ALERT("attach_region: attaching a null region??");
  459.         return 0;
  460.     }
  461.  
  462. again:
  463.     for (i = 0; i < proc->num_reg; i++) {
  464.         if (!proc->mem[i]) {
  465.             assert(proc->addr[i] == 0);
  466.             reg->links++;
  467.             proc->mem[i] = reg;
  468.             proc->addr[i] = (virtaddr) reg->loc;
  469.             mark_proc_region(proc,reg,PROT_P);
  470.             return proc->addr[i];
  471.         }
  472.     }
  473.  
  474. /* Hmmm, OK, we have to expand the process' memory table */
  475.     TRACELOW(("Expanding process memory table"));
  476.     i = proc->num_reg + NUM_REGIONS;
  477.  
  478.     newmem = kmalloc(i * SIZEOF(MEMREGION *));
  479.     newaddr = kmalloc(i * SIZEOF(virtaddr));
  480.  
  481.     if (newmem && newaddr) {
  482.     /*
  483.      * We have to use temps while allocating and freeing mem
  484.      * and addr so the memory protection code won't walk this
  485.      * process' memory list in the middle.
  486.      */
  487.         void *pmem, *paddr;
  488.  
  489.     /* copy over the old address mapping */
  490.         for (i = 0; i < proc->num_reg; i++) {
  491.             newmem[i] = proc->mem[i];
  492.             newaddr[i] = proc->addr[i];
  493.             if (newmem[i] == 0)
  494.                 assert(newaddr[i] == 0);
  495.         }
  496.     /* initialize the rest of the tables */
  497.         for(; i < proc->num_reg + NUM_REGIONS; i++) {
  498.             newmem[i] = 0;
  499.             newaddr[i] = 0;
  500.         }
  501.     /* free the old tables (carefully! for memory protection) */
  502.         pmem = proc->mem;
  503.         paddr = proc->addr;
  504.         proc->mem = NULL;
  505.         proc->addr = NULL;
  506.         kfree(pmem); kfree(paddr);
  507.         proc->mem = newmem;
  508.         proc->addr = newaddr;
  509.         proc->num_reg += NUM_REGIONS;
  510.     /* this time we will succeed */
  511.         goto again;
  512.     } else {
  513.         if (newmem) kfree(newmem);
  514.         if (newaddr) kfree(newaddr);
  515.         DEBUG(("attach_region: failed"));
  516.         return 0;
  517.     }
  518. }
  519.  
  520. /*
  521.  * detach_region(proc, reg): remove region from the procedure's address
  522.  * space. If no more processes reference the region, return it to the
  523.  * system. Note that we search backwards, so that the most recent
  524.  * attachment of memory gets detached!
  525.  */
  526.  
  527. void
  528. detach_region(proc, reg)
  529.     PROC *proc;
  530.     MEMREGION *reg;
  531. {
  532.     int i;
  533.  
  534.     if (!reg) return;
  535.  
  536.     TRACELOW(("detach_region %lx len %lx from pid %d",
  537.         reg->loc, reg->len, proc->pid));
  538.  
  539.     for (i = proc->num_reg - 1; i >= 0; i--) {
  540.         if (proc->mem[i] == reg) {
  541.             reg->links--;
  542.             proc->mem[i] = 0; proc->addr[i] = 0;
  543.             if (reg->links == 0) {
  544.                 free_region(reg);
  545.             }
  546.             else {
  547.                 /* cause curproc's table to be updated */
  548.                 mark_proc_region(proc,reg,PROT_I);
  549.             }
  550.             return;
  551.         }
  552.     }
  553.     DEBUG(("detach_region: region not attached"));
  554. }
  555.  
  556. /*
  557.  * get_region(MMAP map, ulong size, int mode) -- allocate a new region of the
  558.  * given size in the given memory map. if no region big enough is available,
  559.  * return NULL, otherwise return a pointer to the region.
  560.  * "mode" tells us about memory protection modes
  561.  *
  562.  * the "links" field in the region is set to 1
  563.  *
  564.  * BEWARE: new_region may call get_region (indirectly), so we have to be
  565.  * _very_ careful with re-entrancy in this function
  566.  */
  567.  
  568. MEMREGION *
  569. get_region(map, size, mode)
  570.     MMAP map;
  571.     ulong size;
  572.     int mode;
  573. {
  574.     MEMREGION *m, *n;
  575.  
  576.     TRACELOW(("get_region(%s,%lx,%x)",
  577.         (map == ker ? "ker" : (map == core ? "core" : "alt")),
  578.         size, mode));
  579.  
  580. /* precautionary measures */
  581.     if (size == 0) {
  582.         DEBUG(("request for 0 bytes??"));
  583.         size = 1;
  584.     }
  585.  
  586.     size = ROUND(size);
  587.  
  588.     n = *map;
  589.  
  590.     sanity_check(map);
  591. /* exact matches are likely to be rare, so we pre-allocate a new
  592.  * region here; this helps us to avoid re-entrancy problems
  593.  * when new_region calls get_region
  594.  */
  595.     m = new_region();
  596.  
  597.     while (n) {
  598.         if (ISFREE(n)) {
  599.             if (n->len == size) {
  600.                 if (m) dispose_region(m);
  601.                 n->links++;
  602.                 goto win;
  603.             }
  604.             else if (n->len > size) {
  605. /* split a new region, 'm', which will contain the free bytes after n */
  606.                 if (m) {
  607.                     m->next = n->next;
  608.                     n->next = m;
  609.                     m->mflags = n->mflags & M_MAP;
  610.                     m->loc = n->loc + size;
  611.                     m->len = n->len - size;
  612.                     n->len = size;
  613.                     n->links++;
  614.                     goto win;
  615.                 } else {
  616.                     DEBUG(("get_region: no regions left"));
  617.                     return 0;
  618.                 }
  619.             }
  620.         }
  621.         n = n->next;
  622.     }
  623.  
  624.     if (m)
  625.         dispose_region(m);
  626.  
  627.     TRACELOW(("get_region: no memory left in this map"));
  628.     return NULL;
  629.  
  630. win:
  631.     mark_region(n, mode & PROT_PROTMODE);
  632.     if (mode & M_KEEP) n->mflags |= M_KEEP;
  633.  
  634.     return n;
  635. }
  636.  
  637. /*
  638.  * free_region(MEMREGION *reg): free the indicated region. The map
  639.  * in which the region is contained is given by reg->mflags.
  640.  * the caller is responsible for making sure that the region
  641.  * really should be freed, i.e. that reg->links == 0.
  642.  *
  643.  * special things to do:
  644.  * if the region is a shared text region, we must close the
  645.  * associated file descriptor
  646.  */
  647.  
  648. void
  649. free_region(reg)
  650.     MEMREGION *reg;
  651. {
  652.     MMAP map;
  653.     MEMREGION *m;
  654.     SHTEXT *s, **old;
  655.  
  656.     if (!reg) return;
  657.  
  658.     assert(ISFREE(reg));
  659.  
  660.     if (reg->mflags & M_SHTEXT) {
  661.         TRACE(("freeing shared text region"));
  662.         old = &text_reg;
  663.         for(;;) {
  664.             s = *old;
  665.             if (!s) break;
  666.             if (s->text == reg) {
  667.                 if (s->f)
  668.                     do_close(s->f);
  669.                 *old = s->next;
  670.                 kfree(s);
  671.                 break;
  672.             }
  673.             old = &s->next;
  674.         }
  675.         if (!s) {
  676.             DEBUG(("No shared text entry for M_SHTEXT region??"));
  677.         }
  678.     }
  679.  
  680.     if (reg->mflags & M_CORE)
  681.         map = core;
  682.     else if (reg->mflags & M_ALT)
  683.         map = alt;
  684.     else if (reg->mflags & M_KER)
  685.         map = ker;
  686.     else {
  687.         FATAL("free_region: region flags not valid (%x)", reg->mflags);
  688.     }
  689.     reg->mflags &= M_MAP;
  690.  
  691. /* unhook any vectors pointing into this region */
  692.     unlink_vectors(reg->loc, reg->loc + reg->len);
  693.  
  694. /* BUG(?): should invalidate caches entries - a copyback cache could stuff
  695.  * things into freed memory.
  696.  *    cinv(reg->loc, reg->len);
  697.  */
  698.     m = *map;
  699.     assert(m);
  700.  
  701.     /* MEMPROT: invalidate */
  702.     if (map == core || map == alt)
  703.         mark_region(reg,PROT_I);
  704.  
  705.     if (m == reg) goto merge_after;
  706.  
  707. /* merge previous region if it's free and contiguous with 'reg' */
  708.  
  709. /* first, we find the region */
  710.     while (m && m->next != reg)
  711.         m = m->next;
  712.  
  713.     if (m == NULL) {
  714.         FATAL("couldn't find region %lx: loc: %lx len: %ld",
  715.             reg, reg->loc, reg->len);
  716.     }
  717.  
  718.     if (ISFREE(m) && (m->loc + m->len == reg->loc)) {
  719.         m->len += reg->len;
  720.         assert(m->next == reg);
  721.         m->next = reg->next;
  722.         reg->next = 0;
  723.         dispose_region(reg);
  724.         reg = m;
  725.     }
  726.  
  727. /* merge next region if it's free and contiguous with 'reg' */
  728. merge_after:
  729.     m = reg->next;
  730.     if (m && ISFREE(m) && reg->loc + reg->len == m->loc) {
  731.         reg->len += m->len;
  732.         reg->next = m->next;
  733.         m->next = 0;
  734.         dispose_region(m);
  735.     }
  736.  
  737.     sanity_check(map);
  738. }
  739.  
  740. /*
  741.  * shrink_region(MEMREGION *reg, ulong newsize):
  742.  *   shrink region 'reg', so that it is now 'newsize' bytes long.
  743.  *   if 'newsize' is bigger than the region's current size, return EGSBF;
  744.  *   otherwise return 0.
  745.  */
  746.  
  747. long
  748. shrink_region(reg, newsize)
  749.     MEMREGION *reg;
  750.     ulong newsize;
  751. {
  752.     MEMREGION *n;
  753.     ulong diff;
  754.  
  755.  
  756.     newsize = ROUND(newsize);
  757.  
  758.     assert(reg->links > 0);
  759.  
  760.     if (!(reg->mflags & (M_CORE | M_ALT | M_KER))) {
  761.         FATAL("shrink_region: bad region flags (%x)", reg->mflags);
  762.     }
  763.  
  764. /* shrinking to 0 is the same as freeing */
  765.     if (newsize == 0) {
  766.         detach_region(curproc, reg);
  767.         return 0;
  768.     }
  769.  
  770. /* if new size is the same as old size, don't do anything */
  771.     if (newsize == reg->len) {
  772.         return 0;    /* nothing to do */
  773.     }
  774.  
  775.     if (newsize > reg->len) {
  776.         DEBUG(("shrink_region: request to make region bigger"));
  777.         return EGSBF;    /* growth failure */
  778.     }
  779.  
  780. /* OK, we're going to free (reg->len - newsize) bytes at the end of
  781.    this block. If the block after us is already free, simply add the
  782.    space to that block.
  783.  */
  784.     n = reg->next;
  785.     diff = reg->len - newsize;
  786.  
  787.     if (n && ISFREE(n) && reg->loc + reg->len == n->loc) {
  788.         reg->len = newsize;
  789.         n->loc -= diff;
  790.         n->len += diff;
  791.         /* MEMPROT: invalidate the second half */
  792.         /* (part of it is already invalid; that's OK) */
  793.         mark_region(n,PROT_I);
  794.  
  795.         return 0;
  796.     }
  797.     else {
  798.         n = new_region();
  799.         if (!n) {
  800.             DEBUG(("shrink_region: new_region failed"));
  801.             return EINTRN;
  802.         }
  803.         reg->len = newsize;
  804.         n->loc = reg->loc + newsize;
  805.         n->len = diff;
  806.         n->mflags = reg->mflags & M_MAP;
  807.         n->next = reg->next;
  808.         reg->next = n;
  809.         /* MEMPROT: invalidate the new, free region */
  810.         mark_region(n,PROT_I);
  811.     }
  812.     return 0;
  813. }
  814.  
  815. /*
  816.  * max_rsize(map): return the length of the biggest free region
  817.  * in the given memory map, or 0 if no regions remain.
  818.  */
  819.  
  820. long
  821. max_rsize(map)
  822.     MMAP map;
  823. {
  824.     MEMREGION *m;
  825.     long size = 0;
  826.  
  827.     for (m = *map; m; m = m->next) {
  828.         if (ISFREE(m)) {
  829.             if (m->len > size) {
  830.                 size = m->len;
  831.             }
  832.         }
  833.     }
  834.     return size;
  835. }
  836.  
  837. /*
  838.  * tot_rsize(map, flag): if flag == 1, return the total number of bytes in
  839.  * the given memory map; if flag == 0, return only the number of free
  840.  * bytes
  841.  */
  842.  
  843. long
  844. tot_rsize(map, flag)
  845.     MMAP map;
  846.     int flag;
  847. {
  848.     MEMREGION *m;
  849.     long size = 0;
  850.  
  851.     for (m = *map; m; m = m->next) {
  852.         if (flag || ISFREE(m)) {
  853.             size += m->len;
  854.         }
  855.     }
  856.     return size;
  857. }
  858.  
  859. /*
  860.  * alloc_region(MMAP map, ulong size, int mode): allocate a new region and
  861.  * attach it to the current process; returns the address at which the region
  862.  * was attached, or NULL. The mode argument is the memory protection mode to
  863.  * give to get_region, and in turn to mark_region.
  864.  */
  865.  
  866. virtaddr
  867. alloc_region(map, size, mode)
  868.     MMAP map;
  869.     ulong size;
  870.     int mode;
  871. {
  872.     MEMREGION *m;
  873.     PROC *proc = curproc;
  874.     virtaddr v;
  875.  
  876.     TRACELOW(("alloc_region(map,size: %lx,mode: %x)",size,mode));
  877.     if (!size) {
  878.         DEBUG(("alloc_region of zero bytes?!"));
  879.         return 0;
  880.     }
  881.  
  882.     m = get_region(map, size, mode);
  883.     if (!m) {
  884.         TRACELOW(("alloc_region: get_region failed"));
  885.         return 0;
  886.     }
  887.  
  888. /* sanity check: even addresses only, please */
  889.     assert((m->loc & MASKBITS) == 0);
  890.  
  891.     v = attach_region(proc, m);
  892. /* NOTE: get_region returns a region with link count 1; since attach_region
  893.  * increments the link count, we restore it after calling attach_region
  894.  */
  895.     m->links = 1;
  896.     if (!v) {
  897.         m->links = 0;
  898.         free_region(m);
  899.         TRACE(("alloc_region: attach_region failed"));
  900.         return 0;
  901.     }
  902.     return v;
  903. }
  904.  
  905. /*
  906.  * routines for creating a copy of an environment, and a new basepage.
  907.  * note that the memory regions created should immediately be attached to
  908.  * a process! Also note that create_env always operates in ST RAM, but
  909.  * create_base might not.
  910.  */
  911.  
  912. MEMREGION *
  913. create_env(env, flags)
  914.     const char *env;
  915.     ulong flags;
  916. {
  917.     long size;
  918.     MEMREGION *m;
  919.     virtaddr v;
  920.     const char *old;
  921.     char *new;
  922.     short protmode;
  923.  
  924.     if (!env) {
  925.         env = ((BASEPAGE *)curproc->base)->p_env;
  926.             /* duplicate parent's environment */
  927.     }
  928.     size = 2;
  929.     old = env;
  930.     while (*env || *(env+1))
  931.         env++,size++;
  932.  
  933.     protmode = (flags & F_PROTMODE) >> F_PROTSHIFT;
  934.  
  935.     v = alloc_region(core, size, protmode);
  936.     /* if core fails, try alt */
  937.     if (!v)
  938.         v = alloc_region(alt, size, protmode);
  939.  
  940.     if (!v) {
  941.         DEBUG(("create_env: alloc_region failed"));
  942.         return (MEMREGION *)0;
  943.     }
  944.     m = addr2mem(v);
  945.  
  946. /* copy the old environment into the new */
  947.     new = (char *) m->loc;
  948.     TRACE(("copying environment: from %lx to %lx", old, new));
  949.     while (size > 0) {
  950.         *new++ = *old++;
  951.         --size;
  952.     }
  953.     TRACE(("finished copying environment"));
  954.  
  955.     return m;
  956. }
  957.  
  958. MEMREGION *
  959. create_base(cmd, env, flags, prgsize)
  960.     const char *cmd;
  961.     MEMREGION *env;
  962.     ulong flags, prgsize;
  963. {
  964.     long len, coresize, altsize;
  965.     MMAP map;
  966.     MEMREGION *m;
  967.     BASEPAGE *b;
  968.     short protmode;
  969.  
  970. /* if flags & F_ALTLOAD == 1, then we might decide to load in alternate
  971.    RAM if enough is available. "enough" is: if more alt ram than ST ram,
  972.    load there; otherwise, if more than (minalt+1)*128K alt ram available
  973.    for heap space, load in alt ram ("minalt" is the high byte of flags)
  974.  */
  975.     if (flags & F_ALTLOAD) {
  976.         coresize = max_rsize(core);
  977.         altsize = max_rsize(alt);
  978.         if (altsize >= coresize)
  979.             map = alt;
  980.         else {
  981.             len = (flags & F_MINALT) >> 28L;
  982.             len = (len+1)*128*1024L + prgsize + 256;
  983.             if (altsize >= len)
  984.                 map = alt;
  985.             else
  986.                 map = core;
  987.         }
  988.     }
  989.     else
  990.         map = core;
  991.  
  992.     len = max_rsize(map);
  993.     if (curproc->maxmem && len > curproc->maxmem) {
  994.         len = curproc->maxmem;
  995.     }
  996.  
  997.     if (len < prgsize) {
  998.         /* can't possibly load this file in its eligible regions */
  999.         DEBUG(("create_base: max_rsize smaller than prgsize"));
  1000.         return 0;
  1001.     }
  1002.  
  1003. /* make sure that a little bit of memory is left over */
  1004.     if (len > 2*KEEP_MEM) {
  1005.         len -= KEEP_MEM;
  1006.     }
  1007.  
  1008.     protmode = (flags & F_PROTMODE) >> F_PROTSHIFT;
  1009.  
  1010.     m = addr2mem(alloc_region(map, len, protmode));
  1011.     if (!m) {
  1012.         DEBUG(("create_base: alloc_region failed"));
  1013.         return 0;
  1014.     }
  1015.     b = (BASEPAGE *)(m->loc);
  1016.  
  1017.     zero((char *)b, (long)sizeof(BASEPAGE));
  1018.     b->p_lowtpa = (long)b;
  1019.     b->p_hitpa = m->loc + m->len;
  1020.     b->p_env = (char *)env->loc;
  1021.     b->p_flags = flags;
  1022.  
  1023.     if (cmd)
  1024.         strncpy(b->p_cmdlin, cmd, 126);
  1025.     return m;
  1026. }
  1027.  
  1028. /*
  1029.  * load_region(): loads the program with the given file name
  1030.  * into a new region, and returns a pointer to that region. On
  1031.  * an error, returns 0 and leaves the error number in mint_errno.
  1032.  * "env" points to an already set up environment region, as returned
  1033.  * by create_env. On success, "xp" points to the file attributes, which
  1034.  * Pexec has already determined, and "fp" points to the programs
  1035.  * prgflags. "text" is a pointer to a MEMREGION
  1036.  * pointer, which will be set to the region occupied by the shared
  1037.  * text segment of this program (if applicable).
  1038.  */
  1039.  
  1040. MEMREGION *
  1041. load_region(filename, env, cmdlin, xp, text, fp)
  1042.     const char *filename;
  1043.     MEMREGION *env;
  1044.     const char *cmdlin;
  1045.     XATTR *xp;        /* attributes for the file just loaded */
  1046.     MEMREGION **text;    /* set to point to shared text region,
  1047.                    if any */
  1048.     long *fp;        /* prgflags for this file */
  1049. {
  1050.     FILEPTR *f;
  1051.     DEVDRV *dev;
  1052.     MEMREGION *reg, *shtext;
  1053.     BASEPAGE *b;
  1054.     long size, start;
  1055.     FILEHEAD fh;
  1056.  
  1057. /* bug: this should be O_DENYW mode, not O_DENYNONE */
  1058. /* we must use O_DENYNONE because of the desktop and because of the
  1059.  * TOS file system brain-damage
  1060.  */
  1061.     f = do_open(filename, O_DENYNONE | O_EXEC, 0, xp);
  1062.     if (!f) {
  1063.         return 0;        /* mint_errno set by do_open */
  1064.     }
  1065.  
  1066.     dev = f->dev;
  1067.     size = (*dev->read)(f, (void *)&fh, (long)sizeof(fh));
  1068.     if (fh.fmagic != GEMDOS_MAGIC || size != (long)sizeof(fh)) {
  1069.         DEBUG(("load_region: file not executable"));
  1070.         mint_errno = ENOEXEC;
  1071. failed:
  1072.         do_close(f);
  1073.         return 0;
  1074.     }
  1075.  
  1076.     if (((fh.flag & F_PROTMODE) >> F_PROTSHIFT) > PROT_MAX_MODE) {
  1077.         DEBUG (("load_region: invalid protection mode changed to private"));
  1078.         fh.flag = (fh.flag & ~F_PROTMODE) | F_PROT_P;
  1079.     }
  1080.     *fp = fh.flag;
  1081.  
  1082.     if (fh.flag & F_SHTEXT) {
  1083.         TRACE(("loading shared text segment"));
  1084.         shtext = get_text_seg(f, &fh, xp);
  1085.         if (!shtext) {
  1086.             DEBUG(("load_region: unable to get shared text segment"));
  1087. /* mint_errno set in get_text_seg */
  1088.             goto failed;
  1089.         }
  1090.         size = fh.fdata + fh.fbss;
  1091.     } else {
  1092.         size = fh.ftext + fh.fdata + fh.fbss;
  1093.         shtext = 0;
  1094.     }
  1095.  
  1096.     reg = create_base(cmdlin, env, fh.flag, size);
  1097.     if (reg && size+1024L > reg->len) {
  1098.         DEBUG(("load_region: insufficient memory to load"));
  1099.         detach_region(curproc, reg);
  1100.         reg = 0;
  1101.     }
  1102.  
  1103.     if (reg == 0) {
  1104.         if (shtext) {
  1105.             detach_region(curproc, shtext);
  1106.         }
  1107.         mint_errno = ENSMEM;
  1108.         goto failed;
  1109.     }
  1110.  
  1111.     b = (BASEPAGE *)reg->loc;
  1112.     b->p_flags = fh.flag;
  1113.     if (shtext) {
  1114.         b->p_tbase = shtext->loc;
  1115.         b->p_tlen = 0;
  1116.         b->p_dbase = b->p_lowtpa + 256;
  1117.     } else {
  1118.         b->p_tbase = b->p_lowtpa + 256;
  1119.         b->p_tlen = fh.ftext;
  1120.         b->p_dbase = b->p_tbase + b->p_tlen;
  1121.     }
  1122.     b->p_dlen = fh.fdata;
  1123.     b->p_bbase = b->p_dbase + b->p_dlen;
  1124.     b->p_blen = fh.fbss;
  1125.  
  1126. /* if shared text, then we start loading at the end of the
  1127.  * text region, since that is already set up
  1128.  */
  1129.     if (shtext) {
  1130.     /* skip over text info */
  1131.         size = fh.fdata;
  1132.         start = fh.ftext;
  1133.     } else {
  1134.         size = fh.ftext + fh.fdata;
  1135.         start = 0;
  1136.     }
  1137.  
  1138.     mint_errno = (int)load_and_reloc(f, &fh, (char *)b+256, start,
  1139.             size, b);
  1140.  
  1141.     if (mint_errno) {
  1142.         detach_region(curproc, reg);
  1143.         if (shtext) detach_region(curproc, shtext);
  1144.         goto failed;
  1145.     }
  1146.  
  1147.     if (fh.flag & F_FASTLOAD)            /* fastload bit */
  1148.         size = b->p_blen;
  1149.     else
  1150.         size = b->p_hitpa - b->p_bbase;
  1151.     if (size > 0) {
  1152.         start = b->p_bbase;
  1153.         if (start & 1) {
  1154.             *(char *)start = 0;
  1155.             start++;
  1156.             --size;
  1157.         }
  1158.         zero((char *)start, size);
  1159.     }
  1160.  
  1161.     do_close(f);
  1162.     *text = shtext;
  1163.     return reg;
  1164. }
  1165.  
  1166. /*
  1167.  * load_and_reloc(f, fh, where, start, nbytes): load and relocate from
  1168.  * the open GEMDOS executable file f "nbytes" bytes starting at offset
  1169.  * "start" (relative to the end of the file header, i.e. from the first
  1170.  * byte of the actual program image in the file). "where" is the address
  1171.  * in (physical) memory into which the loaded image must be placed; it is
  1172.  * assumed that "where" is big enough to hold "nbytes" bytes!
  1173.  */
  1174.  
  1175. long
  1176. load_and_reloc(f, fh, where, start, nbytes, base)
  1177.     FILEPTR *f;
  1178.     FILEHEAD *fh;
  1179.     char *where;
  1180.     long start;
  1181.     long nbytes;
  1182.     BASEPAGE *base;
  1183. {
  1184.     unsigned char c, *next;
  1185.     long r;
  1186.     DEVDRV *dev;
  1187. #define LRBUFSIZ 8196
  1188.     static unsigned char buffer[LRBUFSIZ];
  1189.     long fixup, size, bytes_read;
  1190.     long reloc;
  1191.  
  1192.  
  1193. TRACE(("load_and_reloc: %ld to %ld at %lx", start, nbytes+start, where));
  1194.     dev = f->dev;
  1195.  
  1196.     r = (*dev->lseek)(f, start+sizeof(FILEHEAD), SEEK_SET);
  1197.     if (r < 0) return r;
  1198.     r = (*dev->read)(f, where, nbytes);
  1199.     if (r != nbytes) {
  1200.         DEBUG(("load_region: unexpected EOF"));
  1201.         return ENOEXEC;
  1202.     }
  1203.  
  1204. /* now do the relocation */
  1205. /* skip over symbol table, etc. */
  1206.     r = (*dev->lseek)(f, sizeof(FILEHEAD) + fh->ftext + fh->fdata +
  1207.             fh->fsym, SEEK_SET);
  1208.     if (r < 0) return ENOEXEC;
  1209.  
  1210.     if (fh->reloc != 0 || (*dev->read)(f, (char *)&fixup, 4L) != 4L
  1211.         || fixup == 0) {
  1212.         return 0;    /* no relocation to be performed */
  1213.     }
  1214.  
  1215.     size = LRBUFSIZ;
  1216.     bytes_read = 0;
  1217.     next = buffer;
  1218.  
  1219.     do {
  1220.         if (fixup >= nbytes + start) {
  1221.             TRACE(("load_region: end of relocation at %ld", fixup));
  1222.             break;
  1223.         }
  1224.         else if (fixup >= start) {
  1225.             reloc = *((long *)(where + fixup - start));
  1226.             if (reloc < fh->ftext) {
  1227.                 reloc += base->p_tbase;
  1228.             } else if (reloc < fh->ftext + fh->fdata && base->p_dbase) {
  1229.                 reloc += base->p_dbase - fh->ftext;
  1230.             } else if (reloc < fh->ftext + fh->fdata + fh->fbss && base->p_bbase) {
  1231.                 reloc += base->p_bbase - (fh->ftext + fh->fdata);
  1232.             } else {
  1233.                 DEBUG(("load_region: bad relocation: %ld", reloc));
  1234.                 if (base->p_dbase)
  1235.                     reloc += base->p_dbase - fh->ftext;    /* assume data reloc */
  1236.                 else if (base->p_bbase)
  1237.                     reloc += base->p_bbase - (fh->ftext + fh->fdata);
  1238.                 else
  1239.                     return ENOEXEC;
  1240.             }
  1241.             *((long *)(where + fixup - start)) = reloc;
  1242.         }
  1243.         do {
  1244.             if (!bytes_read) {
  1245.                 bytes_read =
  1246.                     (*dev->read)(f,(char *)buffer,size);
  1247.                 next = buffer;
  1248.             }
  1249.             if (bytes_read < 0) {
  1250.                 DEBUG(("load_region: EOF in relocation"));
  1251.                 return ENOEXEC;
  1252.             }
  1253.             else if (bytes_read == 0)
  1254.                 c = 0;
  1255.             else {
  1256.                 c = *next++; bytes_read--;
  1257.             }
  1258.             if (c == 1) fixup += 254;
  1259.         } while (c == 1);
  1260.         fixup += ( (unsigned) c) & 0xff;
  1261.     } while (c);
  1262.  
  1263.     return 0;
  1264. }
  1265.  
  1266. /*
  1267.  * function to check for existence of a shared text region
  1268.  * corresponding to file "f", and if none is found, to create one
  1269.  * the memory region being returned is attached to the current
  1270.  * process
  1271.  */
  1272.  
  1273. MEMREGION *
  1274. get_text_seg(f, fh, xp)
  1275.     FILEPTR *f;
  1276.     FILEHEAD *fh;
  1277.     XATTR *xp;
  1278. {
  1279.     SHTEXT *s;
  1280.     MEMREGION *m;
  1281.     long r;
  1282.     BASEPAGE b;
  1283.  
  1284.     s = text_reg;
  1285.  
  1286.     while(s) {
  1287.         if (s->f && samefile(&f->fc, &s->f->fc) &&
  1288.             xp->mtime == s->mtime &&
  1289.             xp->mdate == s->mdate)
  1290.         {
  1291.             m = s->text;
  1292.             if (attach_region(curproc, m)) {
  1293. TRACE(("re-using shared text region %lx", m));
  1294.                 return m;
  1295.             }
  1296.             else {
  1297.                 mint_errno = ENSMEM;
  1298.                 return 0;
  1299.             }
  1300.         }
  1301.         s = s->next;
  1302.     }
  1303.  
  1304. /* hmmm, not found; OK, we'll have to create a new text region */
  1305.  
  1306.     s = kmalloc(SIZEOF(SHTEXT));
  1307.     if (!s) {
  1308.         mint_errno = ENSMEM;
  1309.         return 0;
  1310.     }
  1311.     m = 0;
  1312. /* actually, I can't see why loading in TT RAM is ever undesireable,
  1313.  * since shared text programs should be very clean (and since only
  1314.  * the text segment is going in there). But better safe than sorry.
  1315.  */
  1316.     if (fh->flag & F_ALTLOAD) {
  1317.         m = addr2mem(alloc_region(alt, fh->ftext, PROT_P));
  1318.     }
  1319.     if (!m)
  1320.         m = addr2mem(alloc_region(core, fh->ftext, PROT_P));
  1321.  
  1322.     if (!m) {
  1323.         kfree(s);
  1324.         mint_errno = ENSMEM;
  1325.         return 0;
  1326.     }
  1327.  
  1328. /* set up a fake "basepage" for load_and_reloc
  1329.  * note: the 0 values should make load_and_reloc
  1330.  * barf on any attempts at data relocation, since we have
  1331.  * no data segment
  1332.  */
  1333. TRACE(("attempting to create shared text region"));
  1334.  
  1335.     b.p_tbase = m->loc;
  1336.     b.p_tlen = fh->ftext;
  1337.     b.p_dbase = 0;
  1338.     b.p_dlen = 0;
  1339.     b.p_bbase = b.p_blen = 0;
  1340.  
  1341.     r = load_and_reloc(f, fh, (char *)m->loc, 0, fh->ftext, &b);
  1342.     if (r) {
  1343.         detach_region(curproc, m);
  1344.         kfree(s);
  1345.         return 0;
  1346.     }
  1347.  
  1348. /* region has valid shared text data */
  1349.     m->mflags |= M_SHTEXT;
  1350.  
  1351. /*
  1352.  * KLUDGE: to make sure we always have up to date shared text
  1353.  * info, even across a network, we leave the file passed
  1354.  * to us open with DENYWRITE mode, so that nobody will
  1355.  * modify it.
  1356.  */
  1357.     f->links++;    /* keep the file open longer */
  1358.  
  1359. /* BUG: what if someone already has the file open for
  1360.  * writing? Then we could get screwed...
  1361.  */
  1362.     f->flags = (f->flags & ~O_SHMODE) | O_DENYW;
  1363.     s->f = f;
  1364.     s->text = m;
  1365.     s->next = text_reg;
  1366.     s->mtime = xp->mtime;
  1367.     s->mdate = xp->mdate;
  1368.     text_reg = s;
  1369. TRACE(("shared text region %lx created", m));
  1370.     return m;
  1371. }
  1372.  
  1373. /*
  1374.  * exec_region(p, mem, thread): create a child process out of a mem region
  1375.  * "p" is the process structure set up by the parent; it may be "curproc",
  1376.  * if we're overlaying. "mem" is the loaded memory region returned by
  1377.  * "load region". Any open files (other than the standard handles) owned
  1378.  * by "p" are closed, and if thread !=0 all memory is released; the caller
  1379.  * must explicitly attach the environment and base region. The caller must
  1380.  * also put "p" on the appropriate queue (most likely READY_Q).
  1381.  */
  1382.  
  1383. extern long mint_dos(), mint_bios();
  1384.  
  1385. void rts() {}        /* dummy termination routine */
  1386.  
  1387. PROC *
  1388. exec_region(p, mem, thread)
  1389.     PROC      *p;
  1390.     MEMREGION *mem;
  1391.     int thread;
  1392. {
  1393.     BASEPAGE *b;
  1394.     FILEPTR *f;
  1395.     int i;
  1396.     MEMREGION *m;
  1397.  
  1398.     TRACE(("exec_region"));
  1399.  
  1400.     b = (BASEPAGE *) mem->loc;
  1401.  
  1402.     cpush((void *)b->p_tbase, b->p_tlen);    /* flush cached versions of the text */
  1403.     
  1404. /* set some (undocumented) variables in the basepage */
  1405.     b->p_defdrv = p->curdrv;
  1406.     for (i = 0; i < 6; i++)
  1407.         b->p_devx[i] = i;
  1408.  
  1409.     p->dta = (DTABUF *)(b->p_dta = &b->p_cmdlin[0]);
  1410.     p->base = b;
  1411.  
  1412. /* close extra open files */
  1413.     for (i = MIN_OPEN; i < MAX_OPEN; i++) {
  1414.         if ( (f = p->handle[i]) != 0 && (p->fdflags[i] & FD_CLOEXEC) ) {
  1415.             do_pclose(p, f);
  1416.             p->handle[i] = 0;
  1417.         }
  1418.     }
  1419.  
  1420. /* initialize memory */
  1421.     recalc_maxmem(p);
  1422.     if (p->maxmem) {
  1423.         shrink_region(mem, p->maxmem);
  1424.         b->p_hitpa = b->p_lowtpa + mem->len;
  1425.     }
  1426.  
  1427.     p->memflags = b->p_flags;
  1428.  
  1429.     if (!thread) {
  1430.         for (i = 0; i < p->num_reg; i++) {
  1431.             m = p->mem[i];
  1432.             if (m) {
  1433.                 m->links--;
  1434.                 if (m->links <= 0)
  1435.                     free_region(m);
  1436.             }
  1437.         }
  1438.         if (p->num_reg > NUM_REGIONS) {
  1439.             /*
  1440.              * If the proc struct has a larger mem array than
  1441.              * the default, then free it and allocate a
  1442.              * default-sized one.
  1443.              */
  1444.  
  1445.             /*
  1446.              * hoo ha! Memory protection problem here. Use
  1447.              * temps and pre-clear p->mem so memprot doesn't try
  1448.              * to walk these structures as we're freeing and
  1449.              * reallocating them!  (Calling kmalloc can cause
  1450.              * a table walk if the alloc results in calling
  1451.              * get_region.)
  1452.              */
  1453.             void *pmem, *paddr;
  1454.  
  1455.             pmem = p->mem;
  1456.             paddr = p->addr;
  1457.             p->mem = NULL; p->addr = NULL;
  1458.             kfree(pmem); kfree(paddr);
  1459.  
  1460.             pmem = kmalloc(NUM_REGIONS * SIZEOF(MEMREGION *));
  1461.             paddr = kmalloc(NUM_REGIONS * SIZEOF(virtaddr));
  1462.             assert(pmem && paddr);
  1463.             p->mem = pmem;
  1464.             p->addr = paddr;
  1465.             p->num_reg = NUM_REGIONS;
  1466.         }
  1467.         zero((char *)p->mem, (p->num_reg)*SIZEOF(MEMREGION *));
  1468.         zero((char *)p->addr, (p->num_reg)*SIZEOF(virtaddr));
  1469.     }
  1470.  
  1471. /* initialize signals */
  1472.     p->sigmask = 0;
  1473.     for (i = 0; i < NSIG; i++) {
  1474.         if (p->sighandle[i] != SIG_IGN) {
  1475.             p->sighandle[i] = SIG_DFL;
  1476.             p->sigflags[i] = 0;
  1477.             p->sigextra[i] = 0;
  1478.         }
  1479.     }
  1480.  
  1481. /* zero the user registers, and set the FPU in a "clear" state */
  1482.     for (i = 0; i < 15; i++)
  1483.         p->ctxt[CURRENT].regs[i] = 0;
  1484.     p->ctxt[CURRENT].sr = 0;
  1485.     p->ctxt[CURRENT].fstate[0] = 0;
  1486.  
  1487. /* set PC, stack registers, etc. appropriately */
  1488.     p->ctxt[CURRENT].pc = b->p_tbase;
  1489.  
  1490. /* The "-0x20" is to make sure that syscall.s won't run past the end of
  1491.  * memory when the user makes a system call and doesn't push very many
  1492.  * parameters -- syscall always tries to copy the maximum possible number
  1493.  * of parms.
  1494.  *
  1495.  * NOTE: there's a sanity check here in case programs Mshrink a basepage
  1496.  * without fixing the p_hitpa field in the basepage; this is to ensure
  1497.  * compatibility with older versions of MiNT, which ignore p_hitpa.
  1498.  */
  1499.     if (valid_address(b->p_hitpa - 0x20))
  1500.         p->ctxt[CURRENT].usp = b->p_hitpa - 0x20;
  1501.     else
  1502.         p->ctxt[CURRENT].usp = mem->loc + mem->len - 0x20;
  1503.  
  1504.     p->ctxt[CURRENT].ssp = (long)(p->stack + ISTKSIZE);
  1505.     p->ctxt[CURRENT].term_vec = (long)rts;
  1506.  
  1507. /* set up stack for process */
  1508.     *((long *)(p->ctxt[CURRENT].usp + 4)) = (long) b;
  1509.  
  1510. /* check for a valid text region. some compilers (e.g. Lattice 3) just throw
  1511.    everything into the text region, including data; fork() must be careful
  1512.    to save the whole region, then. We assume that if the compiler (or
  1513.    assembler, or whatever) goes to the trouble of making separate text, data,
  1514.    and bss regions, then the text region is code and isn't modified and
  1515.    fork doesn't have to save it.
  1516.  */
  1517.     if (b->p_blen != 0 || b->p_dlen != 0)
  1518.         p->txtsize = b->p_tlen;
  1519.     else
  1520.         p->txtsize = 0;
  1521.  
  1522. /*
  1523.  * An ugly hack: dLibs tries to poke around in the parent's address space
  1524.  * to find stuff. For now, we'll allow this by faking a pointer into
  1525.  * the parent's address space in the place in the basepage where dLibs is
  1526.  * expecting it. This ugly hack only works correctly if the Pexec'ing
  1527.  * program (i.e. curproc) is in user mode.
  1528.  */
  1529.     if (curproc != rootproc)
  1530.         curproc->base->p_usp = curproc->ctxt[SYSCALL].usp - 0x32;
  1531.  
  1532.     return p;
  1533. }
  1534.  
  1535. /*
  1536.  * misc. utility routines
  1537.  */
  1538.  
  1539. /*
  1540.  * long memused(p): return total memory allocated to process p
  1541.  */
  1542.  
  1543. long
  1544. memused(p)
  1545.     PROC *p;
  1546. {
  1547.     int i;
  1548.     long size;
  1549.  
  1550.     /* a ZOMBIE owns no memory and its mem array ptr is zero */
  1551.     if (p->mem == NULL) return 0;
  1552.  
  1553.     size = 0;
  1554.     for (i = 0; i < p->num_reg; i++) {
  1555.         if (p->mem[i])
  1556.             size += p->mem[i]->len;
  1557.     }
  1558.     return size;
  1559. }
  1560.  
  1561. /* 
  1562.  * recalculate the maximum memory limit on a process; this limit depends
  1563.  * on the max. allocated memory and max. total memory limits set by
  1564.  * p_setlimit (see dos.c), and (perhaps) on the size of the program
  1565.  * that the process is executing. whenever any of these things
  1566.  * change (through p_exec or p_setlimit) this routine must be called
  1567.  */
  1568.  
  1569. void
  1570. recalc_maxmem(p)
  1571.     PROC *p;
  1572. {
  1573.     BASEPAGE *b;
  1574.     long siz;
  1575.  
  1576.     b = (BASEPAGE *)p->base;
  1577.     if (b)
  1578.         siz = b->p_tlen + b->p_dlen + b->p_blen;
  1579.     else
  1580.         siz = 0;
  1581.     p->maxmem = 0;
  1582.     if (p->maxdata) {
  1583.         p->maxmem = p->maxdata + siz;
  1584.     }
  1585.  
  1586.     if (p->maxcore) {
  1587.         if (p->maxmem == 0 || p->maxmem > p->maxcore)
  1588.             p->maxmem = p->maxcore;
  1589.     }
  1590.     if (p->maxmem && p->maxmem < siz)
  1591.         p->maxmem = siz;
  1592. }
  1593.  
  1594. /*
  1595.  * valid_address: checks to see if the indicated address falls within
  1596.  * memory attached to the current process
  1597.  */
  1598.  
  1599. int
  1600. valid_address(addr)
  1601.     long addr;
  1602. {
  1603.     int i;
  1604.     MEMREGION *m;
  1605.  
  1606.     for (i = 0; i < curproc->num_reg; i++) {
  1607.         if ((m = curproc->mem[i]) != 0) {
  1608.             if (addr >= m->loc && addr <= m->loc + m->len)
  1609.                 return 1;
  1610.         }
  1611.     }
  1612.     return 0;
  1613. }
  1614.  
  1615. /*
  1616.  * convert an address to a memory region; this works only in
  1617.  * the ST RAM and TT RAM maps, and will fail for memory that
  1618.  * MiNT doesn't own or which is virtualized
  1619.  */
  1620.  
  1621. MEMREGION *
  1622. addr2region(addr)
  1623.     long addr;
  1624. {
  1625.     unsigned long ua = (unsigned long) addr;
  1626.  
  1627.     extern ulong mint_top_st, mint_top_tt;
  1628.     MEMREGION *r;
  1629.     MMAP map;
  1630.  
  1631.     if (ua < mint_top_st) {
  1632.         map = core;
  1633.     } else if (ua < mint_top_tt) {
  1634.         map = alt;
  1635.     } else {
  1636.         return 0;
  1637.     }
  1638.  
  1639.     for (r = *map; r; r = r->next) {
  1640.         if (addr >= r->loc && addr < r->loc + r->len)
  1641.             return r;
  1642.     }
  1643.     return 0;
  1644. }
  1645.  
  1646. /*
  1647.  * some debugging stuff
  1648.  */
  1649.  
  1650. void
  1651. DUMP_ALL_MEM()
  1652. {
  1653. #ifdef DEBUG_INFO
  1654.     DUMPMEM(ker);
  1655.     DUMPMEM(core);
  1656.     DUMPMEM(alt);
  1657.     FORCE("new memory region descriptor pages: %d", num_reg_requests);
  1658. #endif
  1659. }
  1660.  
  1661. void
  1662. DUMPMEM(map)
  1663.     MMAP map;
  1664. {
  1665. #ifdef DEBUG_INFO
  1666.     MEMREGION *m;
  1667.  
  1668.     m = *map;
  1669.     FORCE("%s memory dump: starting at region %lx",
  1670.         (map == ker ? "ker" : (map == core ? "core" : "alt")), m);
  1671.     while (m) {
  1672.         FORCE("%ld bytes at %lx (%d links); next region %lx", m->len, m->loc,
  1673.             m->links, m->next);
  1674.         m = m->next;
  1675.     }
  1676. #else
  1677.     UNUSED(map);
  1678. #endif
  1679. }
  1680.  
  1681. void
  1682. sanity_check(map)
  1683.     MMAP map;
  1684. {
  1685. #ifdef SANITY_CHECK
  1686.     MEMREGION *m, *nxt;
  1687.     long end;
  1688.  
  1689.     m = *map;
  1690.     while (m) {
  1691.         nxt = m->next;
  1692.         if (nxt) {
  1693.             end = m->loc + m->len;
  1694.             if (m->loc < nxt->loc && end > nxt->loc) {
  1695.                 FATAL("MEMORY CHAIN CORRUPTED");
  1696.             }
  1697.             else if (end == nxt->loc && ISFREE(m) && ISFREE(nxt)) {
  1698.                 ALERT("Continguous memory regions not merged!");
  1699.             }
  1700.         }
  1701.         m = nxt;
  1702.     }
  1703. #else
  1704.     UNUSED(map);
  1705. #endif
  1706. }
  1707.